#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/types.h>
#include <unistd.h>

#include <tplayer.h>
//#include <power_manager_client.h>

#define TOTAL_VIDEO_AUDIO_NUM 100
#define MAX_FILE_NAME_LEN 256
#define FILE_TYPE_NUM 25
#define FILE_TYPE_LEN 10
#define VIDEO_TYPE_NUM 11
#define VIDEO_TYPE_LEN 10
#define USE_REPEAT_RESET_MODE 1
#define HOLD_LAST_PICTURE 1
#define LOOP_PLAY_FLAG 1

typedef struct DemoPlayerContext {
    TPlayer *mTPlayer;
    int mSeekable;
    int mError;
    int mVideoFrameNum;
    bool mPreparedFlag;
    bool mLoopFlag;
    bool mSetLoop;
    bool mComplete;
    char mUrl[512];
    MediaInfo *mMediaInfo;
    char mVideoAudioList[TOTAL_VIDEO_AUDIO_NUM][MAX_FILE_NAME_LEN];
    int mCurPlayIndex;
    int mRealFileNum;
    sem_t mPreparedSem;
} DemoPlayerContext;

//* define commands for user control.
typedef struct Command {
    const char *strCommand;
    int nCommandId;
    const char *strHelpMsg;
} Command;

#define COMMAND_HELP 0x1 //* show help message.
#define COMMAND_QUIT 0x2 //* quit this program.

#define COMMAND_SET_SOURCE 0x101     //* set url of media file.
#define COMMAND_PREPARE 0x102        //* prepare the media file.
#define COMMAND_PLAY 0x103           //* start playback.
#define COMMAND_PAUSE 0x104          //* pause the playback.
#define COMMAND_STOP 0x105           //* stop the playback.
#define COMMAND_SEEKTO 0x106         //* seek to posion, in unit of second.
#define COMMAND_RESET 0x107          //* reset the player
#define COMMAND_SHOW_MEDIAINFO 0x108 //* show media information.
#define COMMAND_SHOW_DURATION 0x109  //* show media duration, in unit of second.
#define COMMAND_SHOW_POSITION 0x110  //* show current play position, in unit of second.
#define COMMAND_SWITCH_AUDIO 0x111   //* switch autio track.
#define COMMAND_PLAY_URL 0x112       // set url and prepare and play
#define COMMAND_SET_VOLUME 0x113     // set the software volume
#define COMMAND_GET_VOLUME 0x114     // get the software volume
#define COMMAND_SET_LOOP 0x115       // set loop play flag,1 means loop play,0 means not loop play
#define COMMAND_SET_SCALEDOWN                                                                                                         \
    0x116 // set video scale down ratio,valid value is:2,4,8 .  2 means 1/2 scaledown,4 means 1/4 scaledown,8 means 1/8 scaledown
#define COMMAND_FAST_FORWARD                                                                                                          \
    0x117 // fast forward,valid value is:2,4,8,16, 2 means 2 times fast forward,4 means 4 times fast forward,8 means 8 times fast
          // forward,16 means 16 times fast forward
#define COMMAND_FAST_BACKWARD                                                                                                         \
    0x118 // fast backward,valid value is:2,4,8,16,2 means 2 times fast backward,4 means 4 times fast backward,8 means 8 times fast
          // backward,16 means 16 times fast backward
#define COMMAND_SET_SRC_RECT 0x119       // set display source crop rect
#define COMMAND_SET_OUTPUT_RECT 0x120    // set display output display rect
#define COMMAND_GET_DISP_FRAMERATE 0x121 //* show the real display framerate

#define CEDARX_UNUSE(param) (void)param

DemoPlayerContext demoPlayer;
int isDir = 0;
int gScreenWidth = 0;
int gScreenHeight = 0;
int gPlayerNum = 0;

/* Signal handler */
void terminate(int sig_no) {
    printf("Got signal %d, exiting ...\n", sig_no);

    if (demoPlayer.mTPlayer != NULL) {
        TPlayerDestroy(demoPlayer.mTPlayer);
        demoPlayer.mTPlayer = NULL;
        printf("TPlayerDestroy() successfully\n");
    }

    sem_destroy(&demoPlayer.mPreparedSem);

    if (demoPlayer.mTPlayer != NULL) {
        TPlayerDestroy(demoPlayer.mTPlayer);
        demoPlayer.mTPlayer = NULL;
        printf("TPlayerDestroy successfully\n");
    }
    printf("destroy tplayer \n");
    printf("tplaydemo exit\n");
    exit(1);
}

void install_sig_handler(void) {
    signal(SIGBUS, terminate);
    signal(SIGFPE, terminate);
    signal(SIGHUP, terminate);
    signal(SIGILL, terminate);
    signal(SIGINT, terminate);
    signal(SIGIOT, terminate);
    signal(SIGPIPE, terminate);
    signal(SIGQUIT, terminate);
    signal(SIGSEGV, terminate);
    signal(SIGSYS, terminate);
    signal(SIGTERM, terminate);
    signal(SIGTRAP, terminate);
    signal(SIGUSR1, terminate);
    signal(SIGUSR2, terminate);
}

//* a callback for tplayer.
int CallbackForTPlayer(void *pUserData, int msg, int param0, void *param1) {
    DemoPlayerContext *pDemoPlayer = (DemoPlayerContext *)pUserData;

    CEDARX_UNUSE(param1);
    switch (msg) {
    case TPLAYER_NOTIFY_PREPARED: {
        printf("TPLAYER_NOTIFY_PREPARED,has prepared.\n");
        sem_post(&pDemoPlayer->mPreparedSem);
        pDemoPlayer->mPreparedFlag = 1;
        break;
    }

    case TPLAYER_NOTIFY_PLAYBACK_COMPLETE: {
        printf("TPLAYER_NOTIFY_PLAYBACK_COMPLETE\n");
        pDemoPlayer->mComplete = 1;
        if (pDemoPlayer->mSetLoop == 1) {
            pDemoPlayer->mLoopFlag = 1;
        } else {
            pDemoPlayer->mLoopFlag = 0;
        }
        // PowerManagerReleaseWakeLock("tplayerdemo");
        break;
    }

    case TPLAYER_NOTIFY_SEEK_COMPLETE: {
        printf("TPLAYER_NOTIFY_SEEK_COMPLETE>>>>info: seek ok.\n");
        break;
    }

    case TPLAYER_NOTIFY_MEDIA_ERROR: {
        switch (param0) {
        case TPLAYER_MEDIA_ERROR_UNKNOWN: {
            printf("erro type:TPLAYER_MEDIA_ERROR_UNKNOWN\n");
            break;
        }
        case TPLAYER_MEDIA_ERROR_UNSUPPORTED: {
            printf("erro type:TPLAYER_MEDIA_ERROR_UNSUPPORTED\n");
            break;
        }
        case TPLAYER_MEDIA_ERROR_IO: {
            printf("erro type:TPLAYER_MEDIA_ERROR_IO\n");
            break;
        }
        }
        printf("TPLAYER_NOTIFY_MEDIA_ERROR\n");
        pDemoPlayer->mError = 1;
        if (pDemoPlayer->mPreparedFlag == 0) {
            printf("recive err when preparing\n");
            sem_post(&pDemoPlayer->mPreparedSem);
        }
        if (pDemoPlayer->mSetLoop == 1) {
            pDemoPlayer->mLoopFlag = 1;
        } else {
            pDemoPlayer->mLoopFlag = 0;
        }
        printf("error: open media source fail.\n");
        break;
    }

    case TPLAYER_NOTIFY_NOT_SEEKABLE: {
        pDemoPlayer->mSeekable = 0;
        printf("info: media source is unseekable.\n");
        break;
    }

    case TPLAYER_NOTIFY_BUFFER_START: {
        printf("have no enough data to play\n");
        break;
    }

    case TPLAYER_NOTIFY_BUFFER_END: {
        printf("have enough data to play again\n");
        break;
    }

    case TPLAYER_NOTIFY_VIDEO_FRAME: {
        // printf("get the decoded video frame\n");
        break;
    }

    case TPLAYER_NOTIFY_AUDIO_FRAME: {
        // printf("get the decoded audio frame\n");
        break;
    }

    case TPLAYER_NOTIFY_SUBTITLE_FRAME: {
        // printf("get the decoded subtitle frame\n");
        break;
    }
    case TPLAYER_NOTYFY_DECODED_VIDEO_SIZE: {
        int w, h;
        w = ((int *)param1)[0]; // real decoded video width
        h = ((int *)param1)[1]; // real decoded video height
        printf("*****tplayerdemo:video decoded width = %d,height = %d", w, h);
        // int divider = 1;
        // if(w>400){
        //    divider = w/400+1;
        //}
        // w = w/divider;
        // h = h/divider;
        printf("real set to display rect:w = %d,h = %d\n", w, h);
        // TPlayerSetSrcRect(pDemoPlayer->mTPlayer, 0, 0, w, h);
    }

    default: {
        printf("warning: unknown callback from Tinaplayer.\n");
        break;
    }
    }
    return 0;
}

int playVideo(DemoPlayerContext *playerContext, char *url, int x, int y, int width, int height) {
    printf("before TPlayerSetDataSource,%d:%s\n", playerContext, url);
    playerContext->mSeekable = 1;
    if (TPlayerReset(playerContext->mTPlayer) != 0) {
        printf("TPlayerReset return fail.\n");
    } else {
        printf("reset the player ok.\n");
    }
    if (TPlayerSetDataSource(playerContext->mTPlayer, url, NULL) != 0) {
        printf("TPlayerSetDataSource return fail.\n");
        return -1;
    } else {
        printf("setDataSource end\n");
    }
    if (TPlayerPrepare(playerContext->mTPlayer) != 0) {
        printf("TPlayerPrepare return fail.\n");
        return -1;
    } else {
        printf("TPlayerPrepare end\n");
    }
    playerContext->mComplete = 0;
    playerContext->mError = 0;
    TPlayerSetDisplayRect(playerContext->mTPlayer, x, y, width, height);
#if LOOP_PLAY_FLAG
#if !USE_REPEAT_RESET_MODE
    TPlayerSetLooping(playerContext->mTPlayer, 1);
#endif
#endif
#if HOLD_LAST_PICTURE
    printf("TPlayerSetHoldLastPicture()\n");
    TPlayerSetHoldLastPicture(playerContext->mTPlayer, 1);
#else
    TPlayerSetHoldLastPicture(playerContext->mTPlayer, 0);
#endif
    if (TPlayerStart(playerContext->mTPlayer) != 0) {
        printf("TPlayerStart() return fail.\n");
        return -1;
    } else {
        printf("started.\n");
    }
    return 0;
}

int createPlayersAndPlayVideos() {
    char *lastStrPos;
    int ret = 0;

    printf("create player\n");
    //* create a player.
    memset(&demoPlayer, 0, sizeof(DemoPlayerContext));
    demoPlayer.mTPlayer = TPlayerCreate(CEDARX_PLAYER);
    if (demoPlayer.mTPlayer == NULL) {
        printf("can not create tplayer, quit.\n");
        return -1;
    } else {
        printf("create player\n");
    }
    //* set callback to player.
    TPlayerSetNotifyCallback(demoPlayer.mTPlayer, CallbackForTPlayer, (void *)&demoPlayer);
    return ret;
}

int tplayer_play(char *url, int x, int y, int width, int height) { return playVideo(&demoPlayer, url, x, y, width, height); }

int tplayer_exit() {
    int i = 0;
    if (demoPlayer.mTPlayer != NULL) {
        TPlayerDestroy(demoPlayer.mTPlayer);
        demoPlayer.mTPlayer = NULL;
        printf("TPlayerDestroy(%d) successfully\n", i);
    }
    printf("destroy all tplayer\n");
}

//* the main method.
int tplayer_init() {
    install_sig_handler();
    sem_init(&demoPlayer.mPreparedSem, 0, 0);
    int ret = 0;
    if (((access("/dev/zero", F_OK)) < 0) || ((access("/dev/fb0", F_OK)) < 0)) {
        printf("/dev/zero OR /dev/fb0 is not exit\n");
    } else {
        system("dd if=/dev/zero of=/dev/fb0"); // clean the framebuffer
    }
    ret = createPlayersAndPlayVideos();

    return ret;
}
